package org.dreamwork.cai.io.impl;

import com.google.gson.Gson;
import org.dreamwork.cai.internal.pojo.JsonInvocationPayload;
import org.dreamwork.cai.internal.pojo.JsonInvocationResult;
import org.dreamwork.cai.proxy.IServiceResolver;
import org.dreamwork.cai.proxy.ProxyInvoker;
import org.dreamwork.gson.GsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

final class SimpleProviderWorker implements Runnable {
    private final Socket socket;
    private final Logger logger = LoggerFactory.getLogger (SimpleProviderWorker.class);
    private final Gson g = GsonHelper.getGson (false, true);

    private final IServiceResolver resolver;

    SimpleProviderWorker (Socket socket, IServiceResolver resolver) {
        this.socket  = socket;
        this.resolver = resolver;
    }

    @Override
    public void run () {
        try {
            ObjectInputStream bis = new ObjectInputStream (socket.getInputStream ());
            int length = bis.readUnsignedShort ();
            byte[] buff = new byte[length];
            if (bis.read (buff) != length) {
                throw new RuntimeException ("expect " + length);
            }

            if (buff.length > 0) {
                String content = new String (buff, StandardCharsets.UTF_8);
                JsonInvocationPayload payload = g.fromJson (content, JsonInvocationPayload.class);
                JsonInvocationResult jir = ProxyInvoker.invoke (resolver, payload);
                content = g.toJson (jir);
                buff = content.getBytes (StandardCharsets.UTF_8);
                OutputStream out = socket.getOutputStream ();
                out.write ((buff.length >> 8) & 0xff);
                out.write (buff.length & 0xff);
                out.write (buff);
                out.flush ();
/*
                if (logger.isTraceEnabled ()) {
                    Gson p = new GsonBuilder ().setPrettyPrinting ().create ();
                    logger.trace ("received request:\r\n{}", p.toJson (payload));
                }

                ServiceRouteFrame local = table.get (resolver.getLocalNamespace ());
                ServiceRouteItem sri = local.get (payload.name);
                MethodInfo info = sri.get (payload.uuid);
                if (info != null) {
                    Object bean = resolver.get (sri.name);
                    Object[] args = new Object[info.parameters.size ()];
                    if (!StringUtil.isEmpty (payload.params)) {
                        Object[] transformed = g.fromJson (payload.params, Object[].class);

                        for (int i = 0; i < info.parameters.size (); i++) {
                            ParameterInfo pi = info.parameters.get (i);
                            Class<?> pt = transformed[i].getClass ();
                            if (pi.javaType.isAssignableFrom (pt)) {
                                args[i] = transformed[i];
                            } else {
                                String temp = g.toJson (transformed[i]);
                                args[i] = g.fromJson (temp, pi.javaType);
                            }
                        }
                    }

                    final JsonInvocationResult jir = invoke (bean, info, args);
                    PrintWriter writer = new PrintWriter (socket.getOutputStream ());
                    g.toJson (jir, writer);
                    writer.flush ();
                    socket.getOutputStream ().flush ();
                } else {
                    logger.warn ("can't find requested service: {}/{}/{}", payload.namespace, payload.name, payload.uuid);
                    final JsonInvocationResult jir = new JsonInvocationResult ();
                    jir.code = 404;
                    jir.error = "Service Not Available";
                    g.toJson (jir, new PrintWriter (socket.getOutputStream ()));
                }
*/
            }
        } catch (IOException ex) {
            logger.warn (ex.getMessage (), ex);
            logger.warn ("job abort by error");
        } finally {
            try {
                socket.close ();
            } catch (IOException ex) {
                logger.warn (ex.getMessage (), ex);
            }
        }
    }

/*
    private Object invoke (Object o, Method m, Object... args) throws Exception {
        if (o == null || m == null) {
            throw new NullPointerException ();
        }

        Class<?> type = o.getClass ();
        Class<?>[] ps = m.getParameterTypes ();
        Method method = type.getDeclaredMethod (m.getName (), ps);
        return method.invoke (o, args);
    }

    private JsonInvocationResult invoke (Object bean, MethodInfo info, Object... args) {
        JsonInvocationResult jir = new JsonInvocationResult ();
        try {
            Object o = invoke (bean, info.method, args);
            jir.result = g.toJson (o);
            jir.code = 0;
        } catch (Exception ex) {
            logger.warn (ex.getMessage (), ex);

            jir.code = -1;
            jir.error = ex.getMessage ();
        }
        return jir;
    }
*/
}